package parser_test

import "github.com/yassinebenaid/bunster/ast"

var ifCommandTests = []testCase{
	{`if cmd; then cmd2; fi`, ast.Script{
		&ast.If{
			Head: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 4}, Name: ast.Word("cmd")},
			},
			Body: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 14}, Name: ast.Word("cmd2")},
			},
		},
	}},
	{`if
		cmd;
	 then
		cmd2;
	fi`, ast.Script{
		&ast.If{
			Head: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 2, Col: 3}, Name: ast.Word("cmd")},
			},
			Body: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 4, Col: 3}, Name: ast.Word("cmd2")},
			},
		},
	}},
	{`if
		cmd1 | cmd2 && cmd3
	 then
		echo 'baz'
	fi`, ast.Script{
		&ast.If{
			Head: []ast.Statement{
				ast.List{
					Left: ast.Pipeline{
						{Command: ast.Command{Position: ast.Position{File: "main.sh", Line: 2, Col: 3}, Name: ast.Word("cmd1")}},
						{Command: ast.Command{Position: ast.Position{File: "main.sh", Line: 2, Col: 10}, Name: ast.Word("cmd2")}},
					},
					Operator: "&&",
					Right:    ast.Command{Position: ast.Position{File: "main.sh", Line: 2, Col: 18}, Name: ast.Word("cmd3")},
				},
			},
			Body: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 4, Col: 3}, Name: ast.Word("echo"), Args: []ast.Expression{ast.Word("baz")}},
			},
		},
	}},
	{`if
		cmd >foo arg <<<"foo bar" |
		cmd2 <input.txt 'foo bar baz' &&
		cmd >foo $var 3<<<"foo bar" |&
		cmd2 "foo bar baz" <input.txt;
	then
		cmd >foo arg <<<"foo bar" |
		cmd2 <input.txt 'foo bar baz' &&
		cmd >foo $var 3<<<"foo bar" |&
		cmd2 "foo bar baz" <input.txt &
	fi;`, ast.Script{
		&ast.If{
			Head: []ast.Statement{
				ast.List{
					Left: ast.Pipeline{
						{
							Command: ast.Command{Position: ast.Position{File: "main.sh", Line: 2, Col: 3},
								Name: ast.Word("cmd"),
								Args: []ast.Expression{ast.Word("arg")},
								Redirections: []ast.Redirection{
									{Src: "1", Method: ">", Dst: ast.Word("foo")},
									{Src: "0", Method: "<<<", Dst: ast.Word("foo bar")},
								},
							},
							Stderr: false,
						},
						{
							Command: ast.Command{Position: ast.Position{File: "main.sh", Line: 3, Col: 3},
								Name: ast.Word("cmd2"),
								Args: []ast.Expression{ast.Word("foo bar baz")},
								Redirections: []ast.Redirection{
									{Src: "0", Method: "<", Dst: ast.Word("input.txt")},
								},
							},
							Stderr: false,
						},
					},
					Operator: "&&",
					Right: ast.Pipeline{
						{
							Command: ast.Command{Position: ast.Position{File: "main.sh", Line: 4, Col: 3},
								Name: ast.Word("cmd"),
								Args: []ast.Expression{ast.Var("var")},
								Redirections: []ast.Redirection{
									{Src: "1", Method: ">", Dst: ast.Word("foo")},
									{Src: "3", Method: "<<<", Dst: ast.Word("foo bar")},
								},
							},
							Stderr: true,
						},
						{
							Command: ast.Command{Position: ast.Position{File: "main.sh", Line: 5, Col: 3},
								Name: ast.Word("cmd2"),
								Args: []ast.Expression{ast.Word("foo bar baz")},
								Redirections: []ast.Redirection{
									{Src: "0", Method: "<", Dst: ast.Word("input.txt")},
								},
							},
							Stderr: false,
						},
					},
				},
			},
			Body: []ast.Statement{
				ast.BackgroundConstruction{
					Statement: ast.List{
						Left: ast.Pipeline{
							{
								Command: ast.Command{Position: ast.Position{File: "main.sh", Line: 7, Col: 3},
									Name: ast.Word("cmd"),
									Args: []ast.Expression{ast.Word("arg")},
									Redirections: []ast.Redirection{
										{Src: "1", Method: ">", Dst: ast.Word("foo")},
										{Src: "0", Method: "<<<", Dst: ast.Word("foo bar")},
									},
								},
								Stderr: false,
							},
							{
								Command: ast.Command{Position: ast.Position{File: "main.sh", Line: 8, Col: 3},
									Name: ast.Word("cmd2"),
									Args: []ast.Expression{ast.Word("foo bar baz")},
									Redirections: []ast.Redirection{
										{Src: "0", Method: "<", Dst: ast.Word("input.txt")},
									},
								},
								Stderr: false,
							},
						},
						Operator: "&&",
						Right: ast.Pipeline{
							{
								Command: ast.Command{Position: ast.Position{File: "main.sh", Line: 9, Col: 3},
									Name: ast.Word("cmd"),
									Args: []ast.Expression{ast.Var("var")},
									Redirections: []ast.Redirection{
										{Src: "1", Method: ">", Dst: ast.Word("foo")},
										{Src: "3", Method: "<<<", Dst: ast.Word("foo bar")},
									},
								},
								Stderr: true,
							},
							{
								Command: ast.Command{Position: ast.Position{File: "main.sh", Line: 10, Col: 3},
									Name: ast.Word("cmd2"),
									Args: []ast.Expression{ast.Word("foo bar baz")},
									Redirections: []ast.Redirection{
										{Src: "0", Method: "<", Dst: ast.Word("input.txt")},
									},
								},
								Stderr: false,
							},
						},
					},
				},
			},
		},
	}},
	{`if cmd; then echo "foo"; fi & if cmd; then cmd; fi & cmd`, ast.Script{
		ast.BackgroundConstruction{
			Statement: &ast.If{
				Head: []ast.Statement{
					ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 4}, Name: ast.Word("cmd")},
				},
				Body: []ast.Statement{
					ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 14}, Name: ast.Word("echo"), Args: []ast.Expression{ast.Word("foo")}},
				},
			},
		},
		ast.BackgroundConstruction{
			Statement: &ast.If{
				Head: []ast.Statement{
					ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 34}, Name: ast.Word("cmd")},
				},
				Body: []ast.Statement{
					ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 44}, Name: ast.Word("cmd")},
				},
			},
		},
		ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 54}, Name: ast.Word("cmd")},
	}},
	{`if cmd; then echo "foo"; fi | if cmd; then echo "foo"; fi |& if cmd; then echo "foo"; fi `, ast.Script{
		ast.Pipeline{
			ast.PipelineCommand{
				Command: &ast.If{
					Head: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 4}, Name: ast.Word("cmd")},
					},
					Body: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 14}, Name: ast.Word("echo"), Args: []ast.Expression{ast.Word("foo")}},
					},
				},
			},
			ast.PipelineCommand{
				Command: &ast.If{
					Head: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 34}, Name: ast.Word("cmd")},
					},
					Body: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 44}, Name: ast.Word("echo"), Args: []ast.Expression{ast.Word("foo")}},
					},
				},
				Stderr: true,
			},
			ast.PipelineCommand{
				Command: &ast.If{
					Head: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 65}, Name: ast.Word("cmd")},
					},
					Body: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 75}, Name: ast.Word("echo"), Args: []ast.Expression{ast.Word("foo")}},
					},
				},
			},
		},
	}},
	{`if cmd; then echo "foo"; fi && if cmd; then echo "foo"; fi;`, ast.Script{
		ast.List{
			Left: &ast.If{
				Head: []ast.Statement{
					ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 4}, Name: ast.Word("cmd")},
				},
				Body: []ast.Statement{
					ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 14}, Name: ast.Word("echo"), Args: []ast.Expression{ast.Word("foo")}},
				},
			},
			Operator: "&&",
			Right: &ast.If{
				Head: []ast.Statement{
					ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 35}, Name: ast.Word("cmd")},
				},
				Body: []ast.Statement{
					ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 45}, Name: ast.Word("echo"), Args: []ast.Expression{ast.Word("foo")}},
				},
			},
		},
	}},
	// Nesting loops
	{`if
		if cmd; then echo "foo"; fi
	then
		if cmd; then echo "foo"; fi
	fi`, ast.Script{
		&ast.If{
			Head: []ast.Statement{
				&ast.If{
					Head: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 2, Col: 6}, Name: ast.Word("cmd")},
					},
					Body: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 2, Col: 16}, Name: ast.Word("echo"), Args: []ast.Expression{ast.Word("foo")}},
					},
				},
			},
			Body: []ast.Statement{
				&ast.If{
					Head: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 4, Col: 6}, Name: ast.Word("cmd")},
					},
					Body: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 4, Col: 16}, Name: ast.Word("echo"), Args: []ast.Expression{ast.Word("foo")}},
					},
				},
			},
		},
	}},
	{`if cmd; then echo "foo"; fi >output.txt <input.txt 2>error.txt >&3 \
	 	>>output.txt <<<input.txt 2>>error.txt &>all.txt &>>all.txt <&4 5<&6`, ast.Script{
		&ast.If{
			Head: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 4}, Name: ast.Word("cmd")},
			},
			Body: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 14}, Name: ast.Word("echo"), Args: []ast.Expression{ast.Word("foo")}},
			},
			Redirections: []ast.Redirection{
				{Src: "1", Method: ">", Dst: ast.Word("output.txt")},
				{Src: "0", Method: "<", Dst: ast.Word("input.txt")},
				{Src: "2", Method: ">", Dst: ast.Word("error.txt")},
				{Src: "1", Method: ">&", Dst: ast.Word("3")},
				{Src: "1", Method: ">>", Dst: ast.Word("output.txt")},
				{Src: "0", Method: "<<<", Dst: ast.Word("input.txt")},
				{Src: "2", Method: ">>", Dst: ast.Word("error.txt")},
				{Method: "&>", Dst: ast.Word("all.txt")},
				{Method: "&>>", Dst: ast.Word("all.txt")},
				{Src: "0", Method: "<&", Dst: ast.Word("4")},
				{Src: "5", Method: "<&", Dst: ast.Word("6")},
			},
		},
	}},
	{"if cmd; then cmd2; fi; if cmd; then cmd2; fi \n  if cmd; then cmd2; fi", ast.Script{
		&ast.If{
			Head: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 4}, Name: ast.Word("cmd")},
			},
			Body: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 14}, Name: ast.Word("cmd2")},
			},
		},
		&ast.If{
			Head: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 27}, Name: ast.Word("cmd")},
			},
			Body: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 37}, Name: ast.Word("cmd2")},
			},
		},
		&ast.If{
			Head: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 2, Col: 6}, Name: ast.Word("cmd")},
			},
			Body: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 2, Col: 16}, Name: ast.Word("cmd2")},
			},
		},
	}},

	{`if cmd; then cmd2; else cmd3; fi`, ast.Script{
		&ast.If{
			Head: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 4}, Name: ast.Word("cmd")},
			},
			Body: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 14}, Name: ast.Word("cmd2")},
			},
			Alternate: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 25}, Name: ast.Word("cmd3")},
			},
		},
	}},
	{`if
		cmd;
	then
		cmd2;
	else
		cmd3;
	fi`, ast.Script{
		&ast.If{
			Head: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 2, Col: 3}, Name: ast.Word("cmd")},
			},
			Body: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 4, Col: 3}, Name: ast.Word("cmd2")},
			},
			Alternate: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 6, Col: 3}, Name: ast.Word("cmd3")},
			},
		},
	}},
	{`if cmd; then cmd2; elif cmd3; then cmd4; fi`, ast.Script{
		&ast.If{
			Head: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 4}, Name: ast.Word("cmd")},
			},
			Body: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 14}, Name: ast.Word("cmd2")},
			},
			Elifs: []ast.Elif{
				{
					Head: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 25}, Name: ast.Word("cmd3")},
					},
					Body: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 36}, Name: ast.Word("cmd4")},
					},
				},
			},
		},
	}},
	{`if cmd; then
		cmd;
	elif cmd; then
		cmd;
	elif cmd; then
		cmd;
	elif cmd; then
		cmd;
	else
		cmd
	fi`, ast.Script{
		&ast.If{
			Head: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 1, Col: 4}, Name: ast.Word("cmd")},
			},
			Body: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 2, Col: 3}, Name: ast.Word("cmd")},
			},
			Elifs: []ast.Elif{
				{
					Head: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 3, Col: 7}, Name: ast.Word("cmd")},
					},
					Body: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 4, Col: 3}, Name: ast.Word("cmd")},
					},
				},
				{
					Head: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 5, Col: 7}, Name: ast.Word("cmd")},
					},
					Body: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 6, Col: 3}, Name: ast.Word("cmd")},
					},
				},
				{
					Head: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 7, Col: 7}, Name: ast.Word("cmd")},
					},
					Body: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 8, Col: 3}, Name: ast.Word("cmd")},
					},
				},
			},
			Alternate: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 10, Col: 3}, Name: ast.Word("cmd")},
			},
		},
	}},
	{`if # comment
		# comment
		# comment
		cmd; # comment
		# comment
		# comment
	 then # comment
		# comment
		# comment
		cmd; # comment
		# comment
		# comment
	elif
		# comment
		# comment
		cmd; # comment
		# comment
		# comment
	then # comment
		# comment
		# comment
		cmd; # comment
		# comment
		# comment
	else # comment
		# comment
		# comment
		cmd # comment
		# comment
		# comment
	fi # comment
	`, ast.Script{
		&ast.If{
			Head: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 4, Col: 3}, Name: ast.Word("cmd")},
			},
			Body: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 10, Col: 3}, Name: ast.Word("cmd")},
			},
			Elifs: []ast.Elif{
				{
					Head: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 16, Col: 3}, Name: ast.Word("cmd")},
					},
					Body: []ast.Statement{
						ast.Command{Position: ast.Position{File: "main.sh", Line: 22, Col: 3}, Name: ast.Word("cmd")},
					},
				},
			},
			Alternate: []ast.Statement{
				ast.Command{Position: ast.Position{File: "main.sh", Line: 28, Col: 3}, Name: ast.Word("cmd")},
			},
		},
	}},
}

var ifCommandErrorHandlingCases = []errorHandlingTestCase{
	{`if`, "main.sh(1:3): syntax error: expected command list after `if`."},
	{`if then`, "main.sh(1:4): syntax error: expected command list after `if`."},
	{`if; then`, "main.sh(1:3): syntax error: expected a valid command name, found `;`."},
	{`if cmd; fi`, "main.sh(1:9): syntax error: expected `then`, found `fi`."},
	{`if fi`, "main.sh(1:4): syntax error: expected command list after `if`."},
	{`if; fi`, "main.sh(1:3): syntax error: expected a valid command name, found `;`."},
	{`if cmd;then fi`, "main.sh(1:13): syntax error: expected command list after `then`."},
	{`if cmd;then cmd`, "main.sh(1:16): syntax error: expected `fi` to close `if` command."},
	{`if cmd;then cmd; fi arg`, "main.sh(1:21): syntax error: unexpected token `arg`."},
	{`if cmd;then cmd; fi <in >out <<<etc arg`, "main.sh(1:37): syntax error: unexpected token `arg`."},

	{`if cmd; then cmd;elif fi`, "main.sh(1:23): syntax error: expected command list after `elif`."},
	{`if cmd; then cmd;elif cmd; fi`, "main.sh(1:28): syntax error: expected `then`, found `fi`."},
	{`if cmd; then cmd;elif cmd; then fi`, "main.sh(1:33): syntax error: expected command list after `then`."},

	{`if cmd;then cmd;else fi`, "main.sh(1:22): syntax error: expected command list after `else`."},
	{`if cmd;then cmd;else; fi`, "main.sh(1:21): syntax error: expected a valid command name, found `;`."},

	{`if cmd|;then cmd; fi`, "main.sh(1:8): syntax error: expected a valid command name, found `;`."},
	{`if cmd| |;then cmd; fi`, "main.sh(1:9): syntax error: expected a valid command name, found `|`."},
	{`if cmd;then cmd|; fi`, "main.sh(1:17): syntax error: expected a valid command name, found `;`."},
	{`if cmd;then cmd| |; fi`, "main.sh(1:18): syntax error: expected a valid command name, found `|`."},
	{`if cmd; then cmd; elif cmd|;then cmd; fi`, "main.sh(1:28): syntax error: expected a valid command name, found `;`."},
	{`if cmd; then cmd; elif cmd| |;then cmd; fi`, "main.sh(1:29): syntax error: expected a valid command name, found `|`."},
	{`if cmd; then cmd; elif cmd;then cmd|; fi`, "main.sh(1:37): syntax error: expected a valid command name, found `;`."},
	{`if cmd; then cmd; elif cmd;then cmd| |; fi`, "main.sh(1:38): syntax error: expected a valid command name, found `|`."},
	{`if cmd; then cmd; else cmd|; fi`, "main.sh(1:28): syntax error: expected a valid command name, found `;`."},
	{`if cmd; then cmd; else cmd| |; fi`, "main.sh(1:29): syntax error: expected a valid command name, found `|`."},

	{`then`, "main.sh(1:1): syntax error: `then` is a reserved keyword, cannot be used as a command name."},
	{`elif`, "main.sh(1:1): syntax error: `elif` is a reserved keyword, cannot be used as a command name."},
	{`else`, "main.sh(1:1): syntax error: `else` is a reserved keyword, cannot be used as a command name."},
	{`fi`, "main.sh(1:1): syntax error: `fi` is a reserved keyword, cannot be used as a command name."},
}
